<template>
  <div class="main">
    <div class="top">
      <div class="left">
        <span>w{{curWeek}}</span>
        <span>D{{curDay}}</span>
      </div>
      <div class="right">
        <span>{{money}}</span>
      </div>
    </div>
    <Cooks  @serving="handleServing" 
            @removecook="fire"
            @addcook="showModel"
            :cooks="cooks"
            :names="nameMap"
            :times="timeMap"
            :cur-time="curTime"
            :max-wait-time="maxDietWaitTime"
            >
    </Cooks>
    <div class="dinner">
      <div class="inside">
        <Customers 
        :list="sitCustomerList" 
        :names="nameMap"
        :times="timeMap"
        :cur-time="curTime"
        :empty-count="maxSitNum - sitCustomerList.length"
        @comfortcustomer="sadLeave($event)"
        @pay="pay($event)"
        >
        </Customers>
      </div>
      <div class="outside">
        <Wait :list="waitCustomerList" @let-customer-sit = "letCustomerSit" :max-wait-len="maxWaitTime" :cur-time="curTime"></Wait>
      </div>
    </div>
    <Modal v-show="isMenuVisible">
      <template v-slot:header>正在点菜</template>
      <template v-slot:body>
          <dl>
            <dt>凉菜(二选一,可不点）</dt>
            <dd v-for="item in dietType['cold']" :key="item.id" class="menu-list">
                <label>
                  <input type="checkbox" :value="item.id" v-model="curOrder">
                  {{ item.name }}
                </label>
                <span>............................${{ item.money }}</span>
            </dd>
            <dt>主菜(五选一，必点)</dt>
            <dd v-for="item in dietType['entree']" :key="item.id" class="menu-list">
                <label>
                  <input type="checkbox" :value="item.id" v-model="curOrder">
                  {{ item.name }}
                </label>
                <span>............................${{ item.money }}</span>
            </dd>
            <dt>饮品(二选一,可不点）</dt>
            <dd v-for="item in dietType['drink']" :key="item.id" class="menu-list">
                <label>
                  <input type="checkbox" :value="item.id" v-model="curOrder">
                  {{ item.name }}
                </label>
                <span>............................${{ item.money }}</span>
            </dd>
          </dl>
      </template>
      <template v-slot:footer>
        <div class="btn-group">
          <button @click="order">点好了，快上菜</button>
          <button @click="leave">不吃了</button>
        </div>
      </template>
    </Modal>
    <Modal v-show="isModalVisible">
      <template v-slot:header> 招聘新厨师 </template>
      <template v-slot:body>
        招聘一名新厨师可以帮你更快地为顾客烹饪菜肴，增加餐厅收入。你最多可以拥有{{maxCookLen}}名厨师。但每个厨师每周需要你支付工资¥{{aWeekSalary}}，请问你确定新招聘一名厨师吗？
      </template>
      <template v-slot:footer>
        <div class="btn-group">
          <button @click="hire">是的，确定招聘</button>
          <button @click="closeModal">先不了</button>
        </div>
      </template>
    </Modal>
    <Modal v-show="isStartModelVisible">
      <template v-slot:header> WebMooc餐厅开业啦！</template>
      <template v-slot:body>
        <p class="first-paragraph">WebMooc餐厅即将开业，请认真经营你的餐厅吧，经营餐厅需要做好下面几件事情！加油！</p>
        <p class="modelDesc"><span>招聘厨师</span><span>迎接客人</span><span>烹饪美食</span></p>
      </template>
      <template v-slot:footer>
        <div class="btn-group start-btn">
          <button @click="start">开始经营吧！</button>
        </div>
      </template>
    </Modal>
    <Notify v-show="isTipVisible">
      <template v-slot:content>
        {{ tipContent }}
      </template>
    </Notify>
  </div>
</template>

<script>
import Cooks from "./Cooks.vue";
import Customers from "./Customers.vue";
import Modal from "./Modal.vue";
import Notify from "./Notify.vue";
import Wait from "./Wait.vue";

export default {
  name: "Main",
  components: {
    Cooks,
    Customers,
    Wait,
    Modal,
    Notify,
  },
  data() {
    return {
      // 记录时间的量
      curTime:0,
      startTime:new Date(),
      money:500,
      isModalVisible: false,
      isTipVisible: false,
      isMenuVisible:false,
      tipContent: "来客人了！快点击客人头像去招待他",
      customerList: [],
      tempCustomerList: [],
      waitCustomerList: [],
      sitCustomerList:[],
      aDaylen: 50,
      aWeekSalary:140,
      maxDietWaitTime:10,
      maxWaitNum:3,
      maxSitNum:4,
      maxWaitTime:20,
      maxCookLen:4,
      orders:[],
      curOrder:[],
      curOrderCustomerId:1,
      dietAndCustomerMap: new Map(),
      timeMap:new Map(),
      nameMap:new Map(),
      moneyMap:new Map(),
      dietSort:{
        cold:[1,2],
        entree:[3,4],
        drink:[8,9]
      },
      dietType:{
        cold:[
          {
            id:1,
            name:'凉拌SAN',
            money:6,
            time:6,
          },
          {
            id:2,
            name:'冷切DOM',
            money:4,
            time:8
          },
        ],
        entree:[
          {
            id:3,
            name:'UL炖LI',
            money:30,
            time:10
          },
          {
            id:4,
            name:'红烧HEAD',
            money:15,
            time:15
          }
        ],
        drink:[
          {
            id:8,
            name:'鲜榨flex',
            money:12,
            time:5
          },
          {
            id:9,
            name:'小程序奶茶',
            money:6,
            time:6
          }
        ]
      },
      cooks:[],
      intervalID:0,
      calcTimes:[0],
      isStartModelVisible:true
    }
  },
  computed:{
    states(){
      return this.cooks.map(item=>item.state)
    },
    curWeek(){
      return Math.floor(this.curTime/(7*this.aDaylen))
    },
    curDay(){
      return Math.floor((this.curTime%(7*this.aDaylen))/this.aDaylen)+1
    },
    calTime(){
      return this.calcTimes.reduce((prev, curr) => prev + curr )
    }
  },
  watch:{
    curDay: function(newVal,oldVal){
      if(newVal == 1 && oldVal == 7){
        let salary = this.cooks.length * this.aWeekSalary
        this.money -= salary
        this.setTip(`支付本周工资$${salary}元`)
      }
      this.createTempCustomer()
      this.createEnterTask()
    },
    orders: function(newVal,oldVal){
      if(newVal.length > oldVal.length){
        this.cooks.forEach((item,idx)=>{
          if(item.state == 0){
            let dietId = newVal.shift()
            this.giveOrderToCooker(idx,dietId)
          }
        })
      }
    },
    states:function(newVal){
      //找找有没有空闲的厨师
      let idx = newVal.findIndex(item=>item == 0)
      // 如果有的话
      if(idx>=0){
        // 如果当前订单的长度>0
        if(this.orders.length){
          let dietId = this.orders.shift()
          this.giveOrderToCooker(idx,dietId)
        }
      }
    }
  },
  methods: {
    start(){
      this.initTimer()
      this.initCooker()
      this.createCustomer()
      this.createTempCustomer()
      this.createEnterTask()
      this.getDietNameMap()
      this.getDietTimeMap()
      this.getMoneyMap()
      this.isStartModelVisible = false
    },
    showModel() {
      this.isModalVisible = true;
    },

    closeModal() {
      this.isModalVisible = false;
    },

    fire(){
      if(this.cooks.length <= 1){
        this.setTip('当前只有一位厨师')
        return;
      }
      let cost = (1+this.curDay/7)* this.aWeekSalary
      
      if(this.money < cost){
        this.setTip('当前的小钱钱数不足以支付遣散费，不能解雇')
        return
      }

      let freeCooker = this.cooks.filter(item => item.state == 0)
      
      if(freeCooker.length>0){
        this.cooks = this.cooks.filter(item => item.id != freeCooker[0].id )
        this.money -= cost
        this.setTip(`解雇成功，支付费用¥${cost}`)
      }else{
        this.setTip('当前没有空闲厨师可以解雇')
      }
      this.closeModal()
    },

    hire(){
      if(this.cooks.length < this.maxCookLen){
        let customeIdRange = this.range(1,this.maxCookLen,1)
        let cookIds = this.cooks.map(item=>item.id)
        let diff = this.getDifference(customeIdRange,cookIds)
        let id = diff[0]
        this.cooks.push(this.createCooker(id))
      }else{
        this.setTip('已经达到厨房上限，放不下那么多人啦')
      }
      this.closeModal()
    },

    // 获得差集
    getDifference(a,b){
      let aSet = new Set(a)
      let bSet = new Set(b)
      return Array.from(new Set(a.concat(b).filter(v => !aSet.has(v) || !bSet.has(v))))
    },

    cost(dietId){
      let money = this.moneyMap.get(dietId)
      if(money){
        let cost = money/2
        this.money -= cost
      }
    },
    
    pay(id){
      let customer = this.sitCustomerList.find(item => item.id == id);
      let orders = customer.orderMap;
      let count = 0
      for (let [key, value] of orders) {
        if(value == 2){
          count += this.moneyMap.get(key)
        }
      }
      this.setTip(`webmooc_${id}支付了¥${count}元`)
      this.money += count
      this.leaveBeforeEat(id)
    },

    // cooker的idx，dietId
    giveOrderToCooker(idx,dietId){
      //给厨师分配菜
      this.cooks[idx].state = 1
      this.cooks[idx].task.set(dietId,false)
      this.cooks[idx].startTime = this.curTime
      // 花钱
      this.cost(dietId)
      // 等待菜完成后，把厨师的任务标记为完成
      this.cook(dietId,idx)
    },
    async customerWaitDiet(cid,dietId){
      await this.wait(this.timeMap.get(dietId)*1.5)
      // 如果这个时候这个订单仍然存在在关系表中的话，则这个菜就超时不要了
      let list = this.dietAndCustomerMap.get(dietId)
      if(list.some(item=>item == cid)){
        // 修改客户的订单状态
        let idx = this.sitCustomerList.findIndex(item=>item.id == cid)
        let map = this.sitCustomerList[idx].orderMap
        map.set(dietId,3)
        this.sitCustomerList[idx].orderMap = new Map(map)
        // 修改客户-订单关系表里面的数据
        list = list.filter(item=>item != cid)
        this.dietAndCustomerMap.set(dietId,list)
      }
    },

    handleServing(idx){
      let cook = this.cooks[idx]
      let cookId = cook.id
      let dieId = Array.from(cook.task)[0][0]
      console.log("handleServing")
      this.servingOrWaste(cookId,dieId)
    },

    async hasCooked(cookId,dietId){
      // 如果厨师还在等待有人去取这道菜的话，则该菜浪费掉
      await this.wait(this.maxDietWaitTime)
      let cooker = this.cooks.find(item => item.id == cookId)
      let cookerDietInfo = Array.from(cooker.task)[0]
      if(cooker.state == 0){
        return 
      }

      if(cookerDietInfo[0]!=dietId){
        return
      }
      // 提醒
      this.setTip(`${this.nameMap.get(dietId)}已经凉啦，不能再端给客人啦`)
    // 让厨师空闲
      this.makeCookFree(cookId)
    },

    servingOrWaste(cookId,dietId){
      // 去获得这个菜的客户list
      let list = this.dietAndCustomerMap.get(dietId);
      // 如果当时还有客户要这个菜的话
      if(list.length>0){
        let cid = -1
        // 则分配菜给指定用户
        for (let index = 0; index < list.length; index++) {
          if(this.customeIsNotEat(list[index])){
            cid = list[index]
            break;
          }
        }
        if(cid!=-1){
          list = list.filter(item=> item != cid)
          this.dietAndCustomerMap.set(dietId,list)
          this.customerGetDiet(cid,dietId)
          this.makeCookFree(cookId)
        }else{
          this.setTip(`点${this.nameMap.get(dietId)}菜的客人正在吃别的菜，请稍等`)
        }
      }else{
        this.makeCookFree(cookId)
        this.setTip(`${this.nameMap.get(dietId)}已经没客人需要啦`)
      }
    },

// 找到第一个能够吃这个菜的客人，要求这个人没有在吃东西
    customeIsNotEat(cid){
      let flag = true
      let map = this.sitCustomerList.find(item=>item.id == cid).orderMap
      let list = Array.from(map)
      if(list.some(item=>item[1] == 1)){
        flag = false
      }
      return flag
    },

    customerGetDiet(cid,dietId){
      // 找到对应的客人
      let idx = this.sitCustomerList.findIndex(item=>item.id == cid);
      // 尝试去修改这个客人的orderMap
      let map = this.sitCustomerList[idx].orderMap
      // 这个dietId对应的菜品改为1
      map.set(dietId,1)
      this.sitCustomerList[idx].orderMap = new Map(map)
      this.eat(cid,dietId)
    },

    async eat(cid,dietId){
      // 找到对应的客人
      let idx = this.sitCustomerList.findIndex(item=>item.id == cid);
      // 去修改这个客人的吃饭时间
      this.sitCustomerList[idx].eatTime = this.curTime
      // 吃饭了很久
      await this.wait(this.timeMap.get(dietId))
      // 尝试去修改这个客人的orderMap
      let map = this.sitCustomerList[idx].orderMap
      // 这个dietId对应的菜品改为2品尝结束
      map.set(dietId,2)
      this.sitCustomerList[idx].orderMap = new Map(map)
    },

    makeCookFree(id){
      let idx = this.cooks.findIndex(item=>item.id == id)
      this.cooks[idx].state = 0
      this.cooks[idx].task = new Map()
    },

    async cook(dietId,idx){
        await this.wait(this.timeMap.get(dietId))
        this.cooks[idx].finishTime = this.curTime
        let map = new Map()
        map.set(dietId,true)
        this.cooks[idx].task = map
        // 在菜做好了之后
        this.hasCooked(this.cooks[idx].id,dietId)
    },

    toggleMenu(){
      if(!this.isMenuVisible){
        // 清理掉这个计时器
        clearInterval(this.intervalID)
        this.calcTimes.push(this.curTime)
      }else{
        this.startTime = new Date()
        this.initTimer()
      }
      this.isMenuVisible = !this.isMenuVisible
    },

    setTip(content) {
      this.tipContent = content;
      this.isTipVisible = true;
      setTimeout(() => {
        this.isTipVisible = false;
      }, 3000);
    },

    // 生成不同id的客人
    createCustomer() {
      //   创建客人
      this.customerList = this.range(1, 7, 1);
    },

// 以Step为步长，从start到stop的范围数字
    range(start,stop,step){
      return Array.from(
          { length: (stop - start) / step + 1 },
          (_, i) => start + i * step
        );
    },

  // 选择今日用餐的客人
    createTempCustomer() {
      class CustomerWithArriveTime {
        constructor(id, arrive_time,interval) {
          this.id = id;
          this.arrive_time = arrive_time;
          this.interval = interval
        }
      }
      let temp = [];
      let sitCustomerListId = this.sitCustomerList.map(item => item.id)
      let list = this.getDifference(this.customerList,sitCustomerListId)
      list.forEach((element) => {
        if(this.whetherGo()){
            let time = Math.floor(this.randn_bm(0, this.aDaylen / 2,1));
            temp.push(new CustomerWithArriveTime(element, this.curTime+time,time));
        }
      });
      // 把TempCustomerList里面的客户都按arrive_time到达
      this.tempCustomerList = temp.sort((a, b) => {
        return a.arrive_time - b.arrive_time;
      });
    },

    async createEnterTask() {
      await Promise.all(
        this.tempCustomerList.map((item) => {
          this.enterTask(item);
        })
      );
    },

    // 到arrive_time的时候就进入等待队列当中,并且删除自身的元素
    async enterTask(customer) {
      await this.wait(customer.interval);
      //从临时的队列当中移除
      if(this.waitCustomerList.length < this.maxWaitNum){
        this.tempCustomerList = this.removeTargetItemById(customer.id,this.tempCustomerList);
        this.waitCustomerList.push(customer);
        this.setTip("客人来了，点击客人头像让客人就坐吧")
        this.isOverWaitTime(customer.id)
      }else{
        this.setTip("当前等待位置不够有一位客人走了")
      }
    },

    // 判断是否已经走了
    async isOverWaitTime(id){
      await this.wait(this.maxWaitTime)
      if(this.waitCustomerList.some(item => item.id == id)){
        this.waitCustomerList = this.removeTargetItemById(id,this.waitCustomerList);
        this.setTip("客人等待时候超时，已经离开")
      }
    },

// 让客人入座
    letCustomerSit(id){
      class SitCustomer{
        constructor(id){
          this.id = id
          this.orderMap = new Map()
          this.serveTimeMap = new Map()
          this.orderTime = 0
          this.eatTime = 0
        }
      }

      if(this.maxSitNum <= this.waitCustomerList.length) return

      this.waitCustomerList = this.removeTargetItemById(id,this.waitCustomerList);
      
      let customer = new SitCustomer(id)
      this.sitCustomerList.push(customer)

      this.curOrderCustomerId = id
      this.toggleMenu()
    },

    order(){
      if(this.checkDiet()){
        // 提交给厨房
        this.orders = this.orders.concat(this.curOrder)
        // 将当前的菜单绑定到对应的客人
        this.setCustomerOderMap()
        // 生成菜品与客人之间的对应关系
        this.createDietAndCustomerMap()
        // 设置客户的下单时间
        this.setCustomerOrderTime()
        // 设置客户的等待
        this.customerStartWait()
        // 清空当前的order
        this.curOrder = []
        // 关闭菜单
        this.toggleMenu()
      }
    },

    setCustomerOrderTime(){
      let idx = this.sitCustomerList.findIndex(item => item.id == this.curOrderCustomerId);
      this.sitCustomerList[idx].orderTime = this.curTime
    },

    customerStartWait(){
      // 先cid,后dieId
      this.curOrder.forEach(item=>{
        this.customerWaitDiet(this.curOrderCustomerId,item)
      })
    },

// 生成菜品与客人之间的对应关系
    createDietAndCustomerMap(){
      let curMap = this.dietAndCustomerMap
      this.curOrder.forEach((item)=>{
        let list = curMap.get(item) || []
        list.push(this.curOrderCustomerId)
        this.dietAndCustomerMap.set(item,list)
      })
    },

// 将当前的菜单绑定到对应的客人
    setCustomerOderMap(){
      let map = new Map()
      this.curOrder.forEach((item)=>map.set(item,0))
      // 用0、1、2、3三种状态分别表示等待、品尝中、品尝结束、逾期了
      // 把当前的map赋值给对应的客人
      let sitCustomerList = this.sitCustomerList
      let index = sitCustomerList.findIndex(item=>item.id == this.curOrderCustomerId)
      this.sitCustomerList[index].orderMap = map
    },

    leave(id){
      this.sitCustomerList = this.removeTargetItemById(id,this.sitCustomerList)
      this.toggleMenu()
    },

    sadLeave(id){
      this.setTip(`motion_${id}什么都没有吃到，伤心的离开了`)
      this.leaveBeforeEat(id)
    },

    leaveBeforeEat(id){
      this.sitCustomerList = this.removeTargetItemById(id,this.sitCustomerList)
    },
//检查菜品是否符合要求
    checkDiet(){
      let coldNum = 0
      let entreeNum = 0
      let drinkNum = 0
      let order = this.curOrder
      order.forEach( id =>{
        let type = this.getDietType(id)
        switch (type){
          case 'cold':
            coldNum++
            break;
          case 'entree':
            entreeNum++
            break;
          case 'drink':
            drinkNum++
            break;
        }
      })
      if(coldNum >= 2){
        this.setTip('凉菜只能二选一')
        return false
      }
      if(entreeNum <= 0){
        this.setTip('主菜必点')
        return false
      }
      if(drinkNum >= 2){
        this.setTip('饮品只能二选一')
        return false
      }

      return true
    },

// 根据菜品id得到菜品所属于的种类
    getDietType(id){
      let dietSort = this.dietSort
      for (const property in dietSort) {
        if(dietSort[property].some(item => item == id)){
          return property
        }
      }
    },

  // 通过id删除指定元素
    removeTargetItemById(id,list){
      list.splice(
        list.findIndex((e) => e.id == id),1
      );
      return list
    },

  // 产生min-max之间的随机数
    random(min, max) {
      let randomNum = Math.floor(Math.random() * (max - min)) + min;
      return randomNum;
    },

    randn_bm(min, max, skew) {
      let u = 0, v = 0;
      while(u === 0) u = Math.random() //Converting [0,1) to (0,1)
      while(v === 0) v = Math.random()
      let num = Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v )
      
      num = num / 10.0 + 0.5 // Translate to 0 -> 1
      if (num > 1 || num < 0) 
        num = this.randn_bm(min, max, skew) // resample between 0 and 1 if out of range
      
      else{
        num = Math.pow(num, skew) // Skew
        num *= max - min // Stretch to fill range
        num += min // offset to min
      }
      return num
    },

  // 决定客人今天是否去吃饭
    whetherGo(){
      let randomNum = Math.random()
      if(randomNum>0.5){
          return true
      }else{
          return false
      }
    },

    // 返回一个计时的Promise
    wait(time) {
      return new Promise((resolve) => setTimeout(resolve, time * 1000));
    },

    getDietNameMap(){
      let dietType = this.dietType
      for (const property in dietType) {
        dietType[property].forEach(item=>{
          this.nameMap.set(item.id,item.name)
        })
      }
    },

    getDietTimeMap(){
      let dietType = this.dietType
      for (const property in dietType) {
        dietType[property].forEach(item=>{
          this.timeMap.set(item.id,item.time)
        })
      }
    },

    getMoneyMap(){
      let dietType = this.dietType
      for (const property in dietType) {
        dietType[property].forEach(item=>{
          this.moneyMap.set(item.id,item.money)
        })
      }
    },

    initCooker(){
      let cook = this.createCooker(1)
      this.cooks.push(cook)
    },

    createCooker(id){
      class Cooker{
        constructor(id){
          this.id = id
          this.state = 0
          this.task = new Map()
          this.startTime = 0
          this.finishTime = 0
        }
      }
      return new Cooker(id)
    },

    getTime(){
      this.curTime = this.calTime + Math.floor((new Date() - this.startTime)/1000)
    },

    initTimer(){
      this.getTime()
      this.intervalID = setInterval(() => {
        this.getTime()
      }, 1000);
    }
  }
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.main {
  display: flex;
  flex-direction: column;
  justify-content: space-between;

  height: 100vh;
  width: 100vw;
  padding: 12px;
  background-image: url("~@/assets/background.jpeg");
  background-repeat: no-repeat;
  background-size: 100vw 100vh;
}

.top {
  display: flex;
  justify-content: space-between;
  margin-bottom: 20px;
}

.left,
.right {
  padding: 10px 44px;
  background-color: #ffd24d;
  border: 12px solid #8c6900;
  color: #8c6900;
  border-radius: 80px;
}

.outside {
  transform: translateY(-30%);
}

.btn-group {
  display: flex;
  justify-content: space-between;
}

.btn-group button {
  padding: 20px 40px;
  background-color: #ffe699;
  border: 6px solid #8c6900;
  color: #8c6900;
  border-radius: 80px;
  font-size: 30px;
}

.menu-list{
  display: flex;
  justify-content: space-between;
}

.start-btn{
  justify-content: center;
}

.start-btn button{
  flex: 1;
}

.modelDesc{
  display: flex;
  justify-content: space-between;
}

.first-paragraph{
  margin-bottom: 20px;
}

/* badge的颜色集合 */
.orange{
  background-color: #ff9122;
}

.blue{
  background-color: #006dd9;
}
</style>
